/*-------------------------------------------------------------------------
 *
 * spi_priv.h
 *				服务器编程接口私有声明
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/executor/spi_priv.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef SPI_PRIV_H
#define SPI_PRIV_H

#include "executor/spi.h"
#include "utils/queryenvironment.h"


#define _SPI_PLAN_MAGIC		569278163

typedef struct
{
	/* 当前结果 */
	uint64		processed;		/* 由执行器提供 */
	SPITupleTable *tuptable;	/* 当前正在构建的元组表 */

	/* 当前执行器调用开始的子事务 */
	SubTransactionId execSubid;

	/* 此执行上下文的资源 */
	slist_head	tuptables;		/* 所有活动的 SPITupleTables 列表 */
	MemoryContext procCxt;		/* 过程上下文 */
	MemoryContext execCxt;		/* 执行器上下文 */
	MemoryContext savedcxt;		/* SPI_connect 调用者的上下文 */
	SubTransactionId connectSubid;	/* 连接子事务的 ID */
	QueryEnvironment *queryEnv; /* SPI 级别的查询环境设置 */

	/* 事务管理支持 */
	bool		atomic;			/* 原子执行上下文，不允许事务 */
	bool		internal_xact;	/* SPI 管理的事务边界，跳过清理 */

	/* 先前嵌套级别的 API 全局变量的保存值 */
	uint64		outer_processed;
	SPITupleTable *outer_tuptable;
	int			outer_result;
} _SPI_connection;

/*
 * SPI 计划有三种状态：已保存、未保存或临时。
 *
 * 通常，_SPI_plan 结构本身以及 argtypes 数组位于由 plancxt 标识的专用内存上下文中（可能很小）。 其他所有附属状态都在由 plancache_list 标识的 plancache 条目中（注意：列表单元格本身位于 plancxt 中）。
 *
 * 在未保存计划中，plancxt 以及 plancache 条目的上下文是 SPI 过程上下文的子级，因此它们在函数退出时都会消失。 plancache.c 也知道 plancache 条目是“未保存的”，因此不将它们链接到其全球列表中；因此它们不会响应无效事件。这是可以的，因为我们假定持有足够的锁以防止其他后端干扰表。
 *
 * 对于已保存的计划，plancxt 被设为 CacheMemoryContext 的子代，因为它应该持续到明确销毁。同样，plancache 条目也将位于 CacheMemoryContext 下，因为我们告知 plancache.c 保存它们。我们依赖 plancache.c 在无效事件的情况下根据需要保持缓存条目更新。
 *
 * 也有“临时”SPI 计划，其中 _SPI_plan 结构甚至没有被 palloc，只存在于某个函数的局部变量中。plancache 条目是未保存的，并存在于 SPI 执行器上下文下，而其他数据如 argtypes 和列表单元格则在 SPI 执行器上下文中是松散的。这样的计划可以通过 plancxt == NULL 来识别。
 *
 * 我们还可以有“一次性”SPI 计划（通常是临时的，如上所述）。这些计划旨在执行一次并丢弃，并且在假设单次使用的基础上进行各种优化。特别注意，在这样的 SPI 计划中的 CachedPlanSources 在执行之前并不是“完整的”。
 *
 * 注意：如果原始查询字符串仅包含空白和注释，则 plancache_list 将为 NIL，因此没有地方存储查询字符串。我们对此不关心，但我们关心参数类型数组，这就是为什么它看似冗余地存储。
 */
typedef struct _SPI_plan
{
	int			magic;			/* 应等于 _SPI_PLAN_MAGIC */
	bool		saved;			/* 已保存或未保存的计划？ */
	bool		oneshot;		/* 一次性计划？ */
	List	   *plancache_list; /* 每个解析树一个 CachedPlanSource */
	MemoryContext plancxt;		/* 包含 _SPI_plan 和数据的上下文 */
	RawParseMode parse_mode;	/* raw_parser() 模式 */
	int			cursor_options; /* 用于规划的游标选项 */
	int			nargs;			/* 计划参数的数量 */
	Oid		   *argtypes;		/* 参数类型（如果 nargs 为 0，则为 NULL） */
	ParserSetupHook parserSetup;	/* 替代参数规格方法 */
	void	   *parserSetupArg;
} _SPI_plan;

#endif							/* SPI_PRIV_H */
